home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Environments / CGIshell 1.3.2 / shell / shell.4th < prev    next >
Encoding:
Text File  |  1996-04-23  |  25.2 KB  |  758 lines  |  [TEXT/ALFA]

  1. \
  2. \
  3. \  PF Forms Handler Shell  --  version 1.3.2
  4. \
  5. \
  6. \  (c) Ronald T. Kneusel, 1995, 1996
  7. \  (rkneusel@post.its.mcw.edu)
  8. \
  9. \  This code may be used and distributed freely provided the copyright 
  10. \  notice remains intact and my name is mentioned in the documentation.
  11. \
  12. \  Last mod: 22-Apr-96
  13. \  =========================================================================
  14. \
  15. \  This file contains all the code in the following files:
  16. \
  17. \                  server.4th   -  web server interface
  18. \                  field.4th    -  new field definition words
  19. \                  template.4th -  template words (new version)
  20. \                  
  21. \  A minimal CGI needs the server.4th file at least.  The others implement more 
  22. \  advanced field processing and template file handling.  They must be loaded 
  23. \  in the order listed.
  24. \
  25. \  Unless space is a consideration, begin your CGI code with this line:
  26. \
  27. \                  --> shell.4th
  28. \
  29. \  to load all segments in the proper order.  The directory OLD contains the 
  30. \  version 1.2 code without the new field definition words.  Use it only if 
  31. \  you have older CGIs to maintain.
  32. \
  33.  
  34. \
  35. \
  36. \  @Field ( addr1 addr2 new|append -- )
  37. \
  38. \      Get the post data string for the field whose address is
  39. \      on the stack.  Place the data into the string at addr2.  @Field 
  40. \      will convert characters as necessary.
  41. \
  42. \  @Addr ( addr new|append -- )
  43. \
  44. \      Put the client's IP address in the string at addr
  45. \
  46. \  @Direct ( addr new|append -- )
  47. \
  48. \      Put the direct argument in the string at addr
  49. \
  50. \  @Browser ( addr new|append -- )
  51. \
  52. \      Put the browser type in the string at addr
  53. \
  54. \  REPLY ( addr -- )
  55. \
  56. \      Send the string back to WebSTAR.  Use only within  ae: ... ;ae
  57. \
  58.  
  59. ( yet more disk I/O words by C. Heilman )
  60.  
  61. \ create space for the fcb and a word to access it
  62. variable FCB 78 allot  ( our File's Control Block )
  63. : +FCB ( offset -- addr ) fcb + ;  ( offset into fcb )
  64.  
  65. \ setup for a (register based) file manager toolbox call
  66. : FTRAP ( -- ) fcb >abs  ,$ 205E ;  ( movea.l [ps]+,a0 )
  67.  
  68. : CLOSE ( -- ) ftrap ,$ A001  ftrap ,$ A013 ;  ( _Close & _FlushBuffer )
  69. : ?DERROR ( -- ) \ report error if result is not zero
  70.     16 +fcb @ ?dup IF  ." DiskError" .  close  abort THEN ;
  71.  
  72. \ open a file with the address of a string of the pathname on the stack
  73. : OPEN ( addr -- ) \ addr is a Forth style string - str[255]
  74.     fcb 80 0 fill           \   clear the fcb for a new file
  75.     >abs  18 +fcb  2!        \  set name of the file to string
  76.     ftrap ,$ A000  ?derror ;  \ _Open the file in the fcb
  77.  
  78. \ create a file
  79. : NEWFILE ( name.addr -- )
  80.     fcb 80 0 fill           \   clear the fcb for a new file
  81.     >abs  18 +fcb  2!        \  set name of the file to string
  82.     ftrap ,$ A008  ?derror    ( _Create )
  83.     ,s TEXT 32 +fcb 2!         \ TEXT type
  84.     ftrap ,$ A00D ?derror ;   ( _SetFileInfo )
  85.  
  86. \ return the filesize !!! MUST BE <32K !!!
  87. : @SIZE ( -- bytes ) ftrap ,$ A011  30 +fcb @ ;  ( _GetEOF )
  88.  
  89. \ set some fcb parameters
  90. : !SIZE ( bytes -- ) 38 +fcb ! ;      \ set bytes-to-read/write
  91. : !BUFF ( addr -- ) >abs 32 +fcb 2! ;  \ set read/write buffer pointer
  92.  
  93. \ read/write with buffer addr and bytes to read/write on the stack
  94. : READ ( addr count -- )  !size !buff ftrap ,$ A002  ?derror ;  ( _Read )
  95. : WRITE ( addr count -- ) !size !buff ftrap ,$ A003  ?derror ;  ( _Write )
  96.  
  97. \ read/write file a byte at a time to/from the stack
  98. : GETCHR ( -- c ) here 1 read  here c@ ;
  99. : PUTCHR ( c -- ) here c!  here 1 write ;
  100.  
  101. \ read until character (c) is encountered
  102. : CREAD ( addr c -- bytes_read )
  103.     44 +fcb c!  128 45 +fcb c!  \ setup ioPosMode
  104.     @SIZE read  42 +fcb @ ;     \ put lowbyte of ioActCount on stack
  105.  
  106. \ A defining word for strings
  107. : $[  \ compiling: ( -- ) enclose a ] terminated string
  108.     CREATE  93 word here  c@ 1+ dup 2 mod +  allot
  109.     DOES>  ;  \ runtime action: ( -- addr ) \ <<-- no count!
  110.  
  111. ( end I/O words )
  112.  
  113.  
  114. ( *************************** String Functions **************************** )
  115. ( Strings 10/15/95 23:30:19 )
  116. \ These words deal with 0 terminated strings.
  117. \
  118. \ The names maintain compatability with the word-set in
  119. \  _Library of Forth Routines and Utilities_  by  James D. Terry
  120. \  (c) 1986 Shadow Lawn Press  ISBN 0-452-25841-3
  121. \
  122. \ In comments, string is the starting address of a zero terminated string,
  123. \ and len is the length not including the zero. String[255] is a length
  124. \ byte preceded string, with a max length of 255 bytes.
  125. \
  126. \ String format:
  127. \ string address is first byte ->This is a string.0<- Ends with a zero
  128.  
  129. \ *** Most of these routines written by C. Heilman ***
  130.  
  131. \ Length and $clear get used a lot - do them in ml.
  132. : LENGTH ( string -- len )  \ length of the string at addr
  133.  ( was:  dup >r BEGIN dup c@ WHILE 1+ REPEAT  r> - ; )
  134.     ,$ 3016          \     move (ps),d0
  135.     ,$ 4a33 ,$ 0000  \ @0: tst.b 0(bp,d0.w)
  136.     ,$ 6706          \     beq.s @1
  137.     ,$ 0640 ,$ 0001  \     addi #1,d0
  138.     ,$ 60f4          \     bra.s @0
  139.     ,$ 9056          \ @1: sub (ps),d0
  140.     ,$ 3c80 ;        \     move d0,(ps)
  141.  
  142. : $CLEAR  ( string -- ) \ erase a string ( equivalent to: 0 swap c! ; )
  143.    ,$ 301E  ,$ 4233 ,$ 0000 ;  \ move (ps)+,d0  clr.b 0(bp,d0.w)
  144.  
  145. \ The next 4 words are directly from Ron's CGI Framework.
  146.  
  147. \ Convert between null terminated and length byte preceeded type strings.
  148. : >NULL ( string[255] -- )  \ convert a string[255] into a string
  149.     dup c@ 2dup + >r swap dup 1+ swap rot cmove  r> $clear ;
  150.  
  151. : >COUNT ( string -- ) \ convert a string into a string[255]
  152.     dup length >r dup dup 1+ r cmove  r> swap c! ;  
  153.  
  154.  
  155. \ Terminal I/O.
  156. : 0TYPE ( string -- )  \ type null terminated string
  157.     dup length dup IF type ELSE 2drop THEN ;
  158.     
  159. : ACCEPT ( string len -- )  \ like expect but stores zero at end of line
  160.     2dup 1+ 0 fill  >r dup r> expect dup length 1- + $clear ; ( bug fixed)
  161.  
  162.  
  163. \ Test a string's content.
  164. : $= ( string1 string2 -- f ) \ true if string2,len2 = string1,len1
  165.     dup length 1+  -1 swap 2swap rot 0 DO  \ set flag to true
  166.       over r + c@  over r + c@  =         \  check each byte
  167.       0= IF rot 1+ rot rot leave THEN    \   change flag to false
  168.     LOOP 2drop ;
  169.  
  170.  
  171. \ Manipulate strings.
  172. : $COPY ( source.string dest.string -- ) \ copy source to dest
  173.     over length 1+ cmove ;
  174.  
  175. : $+ ( source.string dest.string -- ) \ append source to the end of dest
  176.     dup length + $copy ;
  177.  
  178. : $LEFT ( string len -- ) \ clip string to len chars
  179.     over length min  +  $clear ;
  180.  
  181. : $RIGHT ( string len -- ) \ clip string to rightmost len characters
  182.     over length over - 0> IF
  183.       over length over -  rot dup rot +  swap rot 1+  cmove
  184.     ELSE 2drop THEN ;
  185.  
  186. : $MID ( string start len -- ) \ clip string to len section at start
  187.     rot rot over length  swap - 1+  >r dup r> $right  swap $left ;
  188.  
  189. : $UPPER ( string -- ) dup >count  dup upper  dup >null drop ; \ uppercase
  190.  
  191. : $CHAR ( character string -- ) dup length + dup >r c! 0 r> 1+ c! ;
  192.  
  193.  
  194. \ Find and replace with strings.
  195. variable POS  ( local variable )
  196. : $FIND ( string1 string2 -- pos ) \ find string2 in string1; 0 if unfound
  197.     0 pos !
  198.     over length over length - 2+  1  DO
  199.       over here $copy
  200.       here  over length  r swap  $mid
  201.       here over
  202.       $= IF  r pos !  leave THEN
  203.     LOOP  2drop
  204.     pos @ ;
  205.  
  206. : $REPLACE ( dest.string1 find.string2 replace.string3 -- )
  207.     rot >r swap
  208.     r over $find ?dup IF  \   IF string2 is found in string1
  209.       r here $copy         \  THEN replace string2 with string3
  210.       r over 1-  $left      \  modify string1
  211.       rot r $+
  212.       swap length +           \        !!! IMPORTANT !!!
  213.       here length  swap - 1+   \   DOES NOT CHECK FOR OVERWRITE
  214.       here swap $right          \  String1 MUST accomodate any
  215.       here r> $+                 \ additional bytes from string3
  216.     ELSE 2drop r> drop  THEN ;
  217.  
  218. \ Create and assign strings of several varieties.
  219. : $CONSTANT  \ compiling: ( -- ) name a string terminated with '}'
  220.     CREATE  125 word here c@ 1+ dup 2 mod + allot  0 [compile] ,
  221.     DOES>  count drop ;  \ runtime action: ( -- string )
  222. \ This uses a curley brace because they aren't used much on web pages.
  223. \   eg:  $constant ESERROR Empty stack!}
  224.  
  225. : $VARIABLE CREATE 1+ allot ;  \ compiling: ( len -- ) name an empty string
  226. \   eg:  80 $variable INPUTLINE  inputline ${ Hi there!}
  227.  
  228. : $ARRAY \ create named string arrays - name from input stream
  229.     CREATE  dup ,  * allot    \ compiling: ( number_of_.strings len -- )
  230.     DOES>  dup @ rot * + 2+ ;  \ runtime: ( string_number -- string )
  231. \   eg:  15 64 $array ERRORMESSAGES
  232. \        0 errorMessages ${ Error!}
  233.  
  234. \ NOTE: Constants and variables are identical except that constants
  235. \       have no room to grow, but variables _may_ have extra memory
  236. \       allotted to them to grow into.  Also constants are assigned
  237. \       when they are created, while variables (and arrays, which are
  238. \       lists of variables) must be assigned seperately (see below).
  239.  
  240. : ${ ( string -- ) \ assign text to a string from the input stream.
  241.     125 word  here >null  here swap $copy ;
  242. \   eg:  inputLine ${ Something to say!}    *** NO OVERWRITE CHECK ***
  243.  
  244. : MESSAGE[  \ compiling: ( -- ) enclose subsequent ']'ed string
  245.     CREATE  93 word here  c@ 1+ dup 2 mod +  allot  0 [compile] ,
  246.     DOES>  count drop ;  \ runtime action: ( -- addr )
  247.  
  248. : STRING>>  \ compiling: ( n -- )  number of bytes in the string
  249.     CREATE  allot ;
  250.     
  251. : <> = 0= ; macro
  252.  
  253. : newstr  ( addr -- )  \ zero a string
  254.    0 swap c! ;
  255.  
  256. : strcpy ( str1 str2 -- ) \ copy string 1 to string 2
  257.     dup length + >r  \ automatically append
  258.     BEGIN  dup c@ 0 <>  WHILE
  259.       dup c@ r c!  r> 1+ >r  1+
  260.     REPEAT  0 r> c!  ;
  261.  
  262. : strncpy ( str1 str2 -- ) \ copy as above, clear str2 first
  263.     dup newstr  strcpy ;
  264.  
  265. : 0type ( addr -- )  \ type null terminated string
  266.     dup length dup 0 <> IF type ELSE 2drop THEN ;
  267.     
  268. : >null ( addr -- )  \ convert a counted string into a null terminated string
  269.     dup c@ 2dup + >r swap dup 1+ swap rot cmove  0 r> c! ;
  270.  
  271. : >count ( addr -- ) \ convert a null terminated string into a counted string
  272.     dup length >r dup dup 1+ r cmove  r> swap c! ;  
  273.  
  274. : accept ( addr len -- )  \ like expect but no blank at end of line
  275.     swap dup >r swap expect  0 r r> length 1- c! ;
  276.  
  277.  
  278. ( **************** Apple Event and reply string handler ******************* )
  279.  
  280. \ This code courtesy of C. Heilman, slight mods RTK
  281.  
  282. 2variable DDATA  4 allot
  283.  
  284. MESSAGE[ SERROR  Empty stack!]
  285.  
  286. ( get AEDesc handle from an Apple Event )
  287. : ?DESC ( d.key d.type -- desc.handle desc.type -1  or  0 )
  288.     0 >r                                  ( room for error        )
  289.     202 +md 2@ 2>r                        ( the AppleEvent handle )
  290.     2swap 2>r  2>r                        ( keyword and type      )
  291.     here a>r                              ( receiving address     )
  292.     ,$ 303C ,$ 812 ,$ A816 ( AEGetParamDesc: move #$812,d0 _Pack8 )
  293.     r> 0= IF                              ( if there is no error  )
  294.       here 4 + 2@  here 2@  -1            ( get data & leave true )
  295.     ELSE  0 THEN ;                        ( or else leave false   )
  296.  
  297. : -DESC ( addr.where.desc.is.stored -- error ) ( remove desc rec. )
  298.     0 >r  a>r                          ( push room and descriptor )
  299.    ,$ 303C ,$ 0204 ,$ A816 ( AEDisposeDesc: move #$0204,d0 _Pack8 )
  300.     r> ;
  301.  
  302. 2variable DSIZE  \ this double variable holds the size of a string in dbuff
  303. variable  DBUFF 4094 allot  \ this block is filled with a text string
  304.  
  305. ( get AE data from an Apple Event )
  306. : ?DATA ( d.key -- addr  -1  or  0 )
  307.     0 >r               \ make room on stack for error
  308.     202 +md 2@ 2>r      \ push theAppleEvent address
  309.     2>r  ,s TEXT 2>r     \ push keyword (from pstack) and desired type (TEXT)
  310.     here a>r              \ push an address to hold the actual type
  311.     dbuff a>r              \ push the data receiving address
  312.     4096 s>d 2>r            \ max number of bytes to read
  313.     dsize a>r                \ push a variable to hold the actual size
  314.     ,$ 303C ,$ 0E11 ,$ A816   \ AEGetParamPtr: move #$812,d0 _Pack8
  315.     r> 0= IF                   \ if there is no error
  316. \      dbuff  dsize 2@ drop  -1  \ put address, count and true on pstack
  317.        0 dbuff dsize 2@
  318.        drop + c!  dbuff -1      \ make null terminated 
  319.     ELSE  0 THEN ;               \ else false
  320.  
  321. \ Reply to an Apple Event with a string
  322. : REPLY ( addr -- )  \ **** USE INSIDE OF A HANDLER ONLY ****
  323.     dup length                \ how long is it?
  324.     0 >r                      \ put room for error on rstack
  325.     198 +md 2@ 2>r            \ put the ReplyEvent handle on rstack
  326.     ,s ---- 2>r  ,s TEXT 2>r  \ put keyword and type on rstack
  327.     swap a>r  0 2>r           \ put addr & count on rs from pstack
  328.     ,$ 303C ,$ 0A0F ,$ A816   \ AEPutParamPtr: move #$A0F,d0 _Pack8
  329.     r> drop ;                 \ ignore any error
  330.  
  331.  
  332. ( ******************* Words to get field data *********************** )
  333.  
  334.  0 constant NEW     \ start a new string
  335. -1 constant APPEND  \ append at end of existing string
  336.  
  337. variable theAddr    \ holds the address of the string
  338.  
  339. : zeroStr ( -- )  \ zero the string in theAddr
  340.    0 theAddr @  c! ;
  341.  
  342. : >append ( c -- )  \ put a character on the end of theAddr
  343.    theAddr @ length  theAddr @ + dup >r c!     \ character
  344.    0 r> 1+ c! ;  \ null
  345.  
  346. : count>str  ( addr len -- )  \ copy characters into the string
  347.    >r dup r> + swap DO
  348.      r c@ >append
  349.    LOOP ;
  350.  
  351. variable <str>  \ address of target string
  352.  
  353. : h>d ( c -- d )  \ hex digit to decimal, no error checking
  354.    dup 64 > IF  55 -  ELSE  48 -  THEN ;
  355.  
  356. : hex>char ( addr --  )  \ convert a %xx sequence into a character
  357.    1+ dup c@  swap  1+ c@
  358.    h>d swap h>d 16 * +
  359.    dup 32 < IF
  360.      13 = IF  13 <str> @ $CHAR THEN  \ return character
  361.    ELSE
  362.      <str> @ $CHAR  \ anything >= space
  363.    THEN
  364. ;
  365.  
  366. : $copy+ ( s1 len s2 -- )  \ copy s1 to s2 changing %nn codes to characters
  367.    <str> !  \ keep address of target string
  368.    swap dup rot + swap DO
  369.      r c@
  370.      dup 43 = IF  drop  32 <str> @ $CHAR  1  ELSE  \ '+' to space
  371.      dup 37 = IF  drop  r hex>char 3         ELSE  \ %xx
  372.      <str> @ $CHAR  1 THEN THEN                    \ alphanumeric character
  373.    +LOOP
  374. ;
  375.  
  376. create ~cr  3 allot  13 ~cr c! 10 ~cr 1+ c!  0 ~cr 2+ c!
  377. : +crlf  ~cr swap strcpy ;   \ add a <cr><lf> pair
  378.  
  379. message[ rt0 <html>]
  380. message[ rt1 </html>]
  381.  
  382. : startString ( addr -- )  ( load the header text into string ) 
  383.    rt0 swap strcpy ;
  384. : endString ( addr -- ) rt1 swap strcpy ;  ( ending text )
  385.  
  386. ( *************************** Number <--> String ************************* )
  387.  
  388. : f>str ( f addr -- )   \ convert a float to a string in addr
  389.     depth 4 > IF   \ original CH, modified by RTK
  390.       theAddr !  zeroStr \ dest address
  391.       @pen 2>r  10 +md @ >r  30000 10 +md ! \ move pen offscreen
  392.       3000 3000 !pen f.         \ print float: string is at here
  393.       r> 10 +md !  2r> !pen     \ return pen to origonal position
  394.       here count count>str      \ put it addr
  395.     ELSE serror THEN ;
  396.  
  397. create b#! 80 allot  \ buffer for string conversion
  398. : str>f ( addr -- f )  \ convert a string into a float
  399.    dup >r b#! r> length 1+ cmove   \ move to buffer
  400.    b#! 1- >abs fnumber ;  \ and convert
  401.  
  402. ( ********************** User level words ************************* )
  403.  
  404. : @Direct ( addr new|append -- )  \ get the direct argument
  405.    swap theAddr !   \ store the string address
  406.    NEW = IF zeroStr THEN  \ clear the string
  407.    ,s ---- ?data IF  theAddr @ $+  THEN  \ get the argument
  408. ;
  409.  
  410. : @Addr  ( addr new|append -- )  \ get the IP address
  411.    swap theAddr !   \ store the string address
  412.    NEW = IF zeroStr THEN  \ clear the string
  413.    ,s addr ?data IF theAddr @ $+  THEN  \ get it
  414. ;
  415.  
  416. : @Browser ( addr new|append -- )  \ get the browser type
  417.    swap theAddr !   \ store string address
  418.    NEW = IF zeroStr THEN
  419.    ,s Agnt ?data IF  theAddr @ $+  THEN  \ get it
  420. ;
  421.  
  422. variable $fld   \ holds field name
  423. variable $adr   \ holds address
  424. variable $out   \ holds output string
  425. message[ & &]  \ end of field data marker
  426.  
  427. : @Field ( addr1 addr2 new|append -- ) \ get the data for a field
  428.    NEW = IF  swap dup $CLEAR swap  THEN
  429.    $fld !          \ address of field name string
  430.    61 $fld @ $CHAR  \ add an "="
  431.    $out !   \ address of output string
  432.    ,s post ?data IF  \ there is post data
  433.      $adr !
  434.      $adr @ $fld @ $FIND dup 0= IF
  435.        drop         \ no field data
  436.        0 $out @ c!  \ empty string
  437.      ELSE
  438.        1- $fld @ length + $adr @ + \ found the field
  439.        dup & $FIND dup 0= IF
  440.           drop dup length      \ end of string
  441.        ELSE 1- THEN            \ not end of string
  442.        $out @ $copy+           \ put it in the string
  443.      THEN
  444.    THEN
  445.    0 $fld @ dup length 1- + c!  \ remove "="
  446. ;
  447.  
  448. \ on to field.4th
  449.  
  450. \  Field record:
  451. \
  452. \  +----+--------+-----------+---------------+
  453. \  |type| name   | value ... | text ........ |
  454. \  +----+--------+-----------+---------------+
  455. \
  456. \  where:
  457. \
  458. \    type  (1 byte)       =  0 STR, 1 INT, 5 FP
  459. \    name  (30 bytes)     =  null terminated text of field name
  460. \    value (0,2,10 bytes) =  value of field, for STR is same as start of text, 
  461. \                            2 bytes for INT, 10 bytes for FP
  462. \    text  (varies)       =  text string of value, i.e. INT is 2 then text is "2"
  463. \
  464. \  
  465. \  Object:
  466. \
  467. \    Identical to field record but not entered in field array.
  468. \
  469.  
  470. \ *** None of these words check for overflow or error conditions!  Memory is 
  471. \     at a premium, so you, the programmer, are on your own!
  472.  
  473.  
  474. \ Misc support words
  475.  
  476. : notvalid? ( c -- t|f ) \ true if c not a valid number character
  477.    dup 45 = IF drop 0 exit THEN  \ is it '-'?
  478.    dup 46 = IF drop 0 exit THEN  \ '.'
  479.    dup 43 = IF drop 0 exit THEN  \ '+'
  480.    dup 69 = IF drop 0 exit THEN  \ 'E'
  481.    dup 101 = IF drop 0 exit THEN \ 'e'
  482.    dup 47 > swap 58 < and IF 0 exit THEN  \ '0' through '9'
  483.    -1  \ something else
  484. ;
  485.  
  486. : ok? ( s -- s t|f )  \ true if string a valid number
  487.    dup c@ 0= IF 0
  488.    ELSE  \ not null
  489.      dup dup dup length + swap
  490.      DO
  491.        r c@ notvalid? IF
  492.         0 10000 ELSE 1 THEN
  493.      +LOOP  dup 0= IF ( 0 ) ELSE -1 THEN
  494.    THEN
  495. ;
  496.  
  497. variable #digits   \ holds number of significant digits
  498. 6 #digits !        \ default to 6 digits
  499. : f< ( f1 f2 -- f1<f2 ) fcompare >r fdrop fdrop r> -1 = ;
  500. : f> ( f1 f2 -- f1>f2 ) fcompare >r fdrop fdrop r>  1 = ;
  501. : pp ( f -- f )  \ set the output number format
  502.    fdup fabs fdup
  503.    0.009 f> >r  100000.0 f< r> and
  504.    IF  #digits @ fix  ELSE  #digits @ sci  THEN ;
  505.  
  506.  
  507. \ Data types
  508.  
  509. 0 constant STR    \ string
  510. 1 constant INT    \ integer
  511. 5 constant FP     \ floating point
  512.  
  513. \ Record access
  514.  
  515. : .type ( r -- t )  c@ ;      \ return data type
  516. : .name ( r -- a )  1+ ;      \ return address of field name
  517.  
  518. : .val  ( r -- a )  31 + ;    \ return *address* of value
  519.  
  520. : @val ( r -- v )  \  return *value* of field, addr if STR
  521.   dup >r .val r> c@
  522.   dup STR = IF  drop      ELSE  \ STR
  523.   dup INT = IF  drop  @   ELSE  \ INT
  524.   dup FP  = IF  drop  f@  ELSE  \ FP
  525.   drop drop 0 THEN THEN THEN  \ error
  526. ;
  527.  
  528.  
  529. : $%int ( r+31 -- )  \ take int value and put in text area as a string
  530.   dup 2+ >r  @ 0 d>f 0 fix r> f>str ;
  531.  
  532. : $%fp  ( r+31 -- )  \ take fp value and put in text area as a string
  533.   dup 10 + >r  f@ pp r> f>str ;
  534.  
  535. : !val ( v r -- )  \ put the value, by type, in the record
  536.   dup >r .val r> c@
  537.   dup STR = IF  drop dup 0 swap c! strcpy  ELSE  \ STR, copy string
  538.   dup INT = IF  drop dup >r ! r> $%int    ELSE   \ INT
  539.   dup FP  = IF  drop dup >r f! r> $%fp    ELSE   \ FP
  540.   drop drop THEN THEN THEN     \ error
  541. ;
  542.  
  543. : .text ( r -- a )  \ return the *address* of the field text
  544.   dup c@
  545.   dup STR = IF  drop 31 +  ELSE \ STR
  546.   dup INT = IF  drop 33 +  ELSE \ INT
  547.   dup FP  = IF  drop 41 +  ELSE \ FP
  548.   drop drop 0 THEN THEN THEN  \ error
  549. ;
  550.  
  551.  
  552. \ Template and Field array words
  553.  
  554. create (T)  50 2* allot   \ template array
  555. create (F)  50 2* allot   \ field array
  556.  
  557. variable #T#  0 #T# ! \ template array index
  558. variable #F#  0 #F# ! \ field array index
  559.  
  560. : >table ( r_addr -- )  \ enter record in the table
  561.   #T# @ 2* (T) + !  #T# @ 1+ #T# ! ;
  562.  
  563. : >field ( r_addr -- )  \ enter record in the field array
  564.   #F# @ 2* (F) + !  #F# @ 1+ #F# !  ;
  565.  
  566. : @(T) ( idx -- addr )  2* (T) + @ ;
  567. : @(F) ( idx -- addr )  2* (F) + @ ;
  568.  
  569.  
  570. \ Define a field record
  571.  
  572. 30 $variable @#$
  573. : " ( string -- ) \ assign text to a string from the input stream.
  574.     @#$ 34 word  here >null  here swap $copy ;
  575.  
  576. : #FIELD \ define a field record
  577.   CREATE  here >r swap dup >r  2* + 31 + allot  
  578.   ( compiling: type text-size -- addr )
  579.   r> ( type)  r> ( addr)
  580.   2dup c!  ( set type )
  581.   swap drop dup >r 1+  @#$ swap  strcpy  ( set name)
  582.   r  >table ( enter in template array)
  583.   r> >field ; ( enter in fields array)
  584.   ( runtime:  -- addr )
  585.  
  586.  
  587. \
  588. \ E.g.  A floating point field 15 characters long named HEIGHT is defined as:
  589. \
  590. \  FP 15 " height" #FIELD height
  591. \
  592.  
  593. : #OBJECT \ define an object
  594.   CREATE  here >r swap dup >r  2* + 31 + allot  
  595.   ( compiling: type text-size -- addr )
  596.   r> ( type)  r> ( addr)
  597.   2dup c!  ( set type )
  598.   swap drop dup >r 1+  @#$ swap  strcpy  ( set name)
  599.   r>  >table ; ( enter in template array)
  600.   ( runtime:  -- addr )
  601.  
  602. \
  603. \ E.g.  A floating point object 15 characters long named WIDTH is defined as:
  604. \
  605. \  FP 15 " width" #OBJECT width
  606. \
  607.  
  608.  
  609.  
  610. \ Initialize the fields
  611.  
  612. : <<int ( idx -- )  \ put the integer string in the integer part
  613.   @(F) dup 33 +  ok? IF str>f f>d drop ELSE 0 THEN  swap 31 + ! ;
  614.  
  615. : <<fp  ( idx -- )  \ put the float string in the float part
  616.   @(F) dup >r 41 +  ok? IF str>f ELSE 0.0 THEN  r> 31 + f! ;
  617.  
  618. : <getFields> ( -- )  \ get the fields from the Apple Event and initialize
  619.   #F# @ 0 DO                           \ for each field
  620.     r @(F) 1+                          \    get the name
  621.     r @(F) .text swap NEW              \    and the target 
  622.     @Field                             \    fill in the initial string value
  623.     r @(F) c@                          \    get the type
  624.     dup 0=  IF  drop           ELSE    \      STR, nothing to do
  625.     dup 1 = IF  drop  r <<int  ELSE    \      INT, get integer from string
  626.     dup 5 = IF  drop  r <<fp   ELSE    \      FP, get float from string
  627.     drop THEN THEN THEN
  628.   LOOP                                 \ move to the next field
  629. ;
  630.  
  631.  
  632. \ on to template.4th...
  633.  
  634. \  This code handles the reading and evaluation of the template HTML file.
  635. \
  636. \  A template file is an external text file that contains the HTML source to 
  637. \  be returned by the CGI with named markers where calculated values are to 
  638. \  be substituted:
  639. \
  640. \  <h1>A Reply</h1><hr>
  641. \  You are: `name` <p>
  642. \  Age: `age` <p>
  643. \  Weight: `weight` <p>
  644. \
  645. \  The above will look for fields or named strings with the names "name", "age", 
  646. \  and "weight" and substitute their value in the reply string.
  647.  
  648. \ *** None of these words check for overflow or error conditions!  Memory is 
  649. \     at a premium, so you, the programmer, are on your own!
  650.  
  651.  
  652. 1024 constant buffSize        \ size of input buffer
  653. create rwbuff buffSize allot  \ the buffer
  654. variable ~i                   \ start by filling the buffer
  655. variable ~f                   \ number of bytes into the file
  656.  
  657. : fillbuff ( -- )  \ call this after OPEN to use the buffer
  658.    1024 ~i !  0 ~f !  rwbuff buffSize 0 fill ;
  659.  
  660. : getch ( -- c )  \ read a character from the buffer
  661.    ~i @ dup 1024 < IF
  662.      rwbuff + c@  1 ~i +!  1 ~f +!
  663.    ELSE
  664.      drop 0 ~i !  \ clear index
  665.      rwbuff buffSize 0 fill
  666.      @size  ~f @ - 3 -  dup 1 < IF
  667.        drop  -1  \ nothing to read, return -1
  668.      ELSE
  669.        buffSize  min  \ # bytes to read
  670.        rwbuff swap READ
  671.        rwbuff c@  1 ~i +!
  672.      THEN
  673.    THEN ;
  674.  
  675. ( string array words )
  676.  
  677. : array>> ( #elements --  )  \ create an array of #elements
  678.    create 2* allot ;
  679.  
  680. : !array ( data index array -- )  \ store in index
  681.    swap 2* + ! ;
  682.  
  683. : @array ( index array -- data )  \ get data in an array
  684.    swap 2* + @ ;
  685.  
  686. : >array ( 00 01 .. n array -- )  \ store entire array
  687.    swap -1 swap 1- DO
  688.      dup rot swap r swap !array
  689.    -1 +LOOP ;
  690.  
  691. \ add these to the number convert
  692.  
  693. : n>str ( n s -- )  \ integer to string
  694.    >r 0 d>f r> 0 fix f>str ;
  695.  
  696. : str>num ( s -- n )  \ string to integer
  697.    str>f f>d drop ;
  698.  
  699. variable ~output  \ output string
  700. variable ~fname   \ filename string
  701. variable ~length  \ hold length of output string
  702.  
  703. create !@#  32 allot  \ hold the name
  704. create #@!  0 , 0 ,   \ null string
  705.  
  706. : >!@# ( c -- )  \ append a character to !@#
  707.   !@# length  !@# + dup >r c!     \ character
  708.    0 r> 1+ c! ;  \ null
  709.  
  710. : token ( -- )  \ load !@# with name of token
  711.    0 !@# c!                       \ clear !@#
  712.    BEGIN 
  713.      getch dup 96 = 0=            \ while not ` (backquote)
  714.    WHILE  >!@#  REPEAT            \ append to !@#
  715.    drop
  716. ;
  717.  
  718. : lookup ( -- addr )  \ lookup the token name and return address of text part
  719.   #T# @ 0 DO
  720.     r @(T) .name !@#
  721.     $= IF
  722.       r @(T) .text \ got it
  723.       10000
  724.     ELSE 1 THEN
  725.   +LOOP
  726. ;
  727.  
  728. : >>output  ( c -- )  \ append a character
  729.   ~output @ ~length @ + dup >r c!  0 r> 1+ c! 1 ~length +! ; macro
  730.  
  731. : template ( output fname new|append -- )
  732.    \ process a template file
  733.    >r  ~fname !  ~output !  r>
  734.    IF 0 ~output @ c!  0 ~length !        \ zero the string
  735.    ELSE ~output @ length ~length ! THEN  \ append to string 
  736.    ~fname @ open                         \ open the file
  737.    fillbuff                              \ init input buffer
  738.    @size 0 DO   
  739.      getch                               \ get a character
  740.      dup 96 = IF                         \ check for ` (backquote)
  741.       drop                               \ lose the `
  742.       token                              \ get the name 
  743.       lookup                             \ and lookup the string
  744.       ~output @ strcpy                   \ and put it in
  745.       ~output @ length ~length !         \ adjust length
  746.       !@# length 1+                      \ length of name+1 (for backquote)
  747.      ELSE
  748.       dup 31 > IF
  749.        >>output                          \ append to output
  750.       ELSE drop THEN 1
  751.      THEN
  752.    +LOOP
  753.    close                                 \ close the file
  754. ;
  755.  
  756. \ that's all folks!
  757.